﻿using PBO.Data;
using PBO.Util;
using PBO.Network;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using PBO.Network.Domain;
using PBO.Network.Enumerations;
using PBO.Network.Server;

namespace PBO.Forms.NetworkForm.BattleForm
{
    public class BattleServerForm : BattleForm
    {
        private BattleRule[] _battleRules;

#warning rnd seed
        private int _seed = unchecked((int)DateTime.Now.Ticks);

        private PokemonBattleServer _server;
        private Dictionary<byte, PlayerInfo> _teams = new Dictionary<byte, PlayerInfo>();
        private int _agreeTieCounter;

        private bool _tieing;
        private object _locker = new object();

        public BattleServerForm(TeamData myTeamValue, string name, BattleMode battleMode, BattleRule[] rules)
            : base(name, 1, myTeamValue, battleMode)
        {
            this.Load += BattleServerForm_Load;

            _myTeamData = myTeamValue;
            _battleRules = rules;

            SetCaption("对战窗口");
        }

        private void BattleServerForm_Load(object sender, System.EventArgs e)
        {
            InitializeServer();
            RunServer();
        }

        private void InitializeServer()
        {
            SetProgressText("正在初始化");

            _server = new PokemonBattleServer();
            _server.OnBattleClientConnected += VerifyClient;
            _server.OnSetTeam += ReceiveClientTeam;
            _server.OnSetMove += OnGetMove;
            _server.OnPlayerExit += OnExit;
            _server.OnTie += OnTie;
            _server.OnRequestBattleInfo += GetBattleInfo;

            SetRules(_battleRules);
            SetRandomSeed(_seed);

            _teams[_myPosition] = new PlayerInfo(_identity, _myTeamData.ToBytes());
        }
        protected virtual void RunServer()
        {
            if (_server.Initialize())
            {
                _server.RunThread();
                SetProgressText("正在等待连接");
            }
            else
            {
                ModShare.ShowInformation(this, "服务器初始化失败", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        protected virtual bool VerifyClient(string identity, BattleMode mode, string versionInfo, out string message)
        {
            message = "";
            if (mode != _battleMode)
            {
                message = "对战模式不同";
                return false;
            }
            if (versionInfo != ModShare.GetVersionInfo())
            {
                message = "版本不同";
                return false;
            }
            lock (_locker)
            {
                if (PlayerReady)
                {
                    message = "人数已满";
                    return false;
                }
            }
            return true;
        }
        protected virtual void ReceiveClientTeam(byte position, string identity, byte[] team)
        {
            _teams[position] = new PlayerInfo(identity, team);

            lock (_locker)
            {
                SetPlayer(position, identity);
                if (PlayerReady)
                {
                    InitializeBattle();
                }
            }
        }

        protected void InitializeBattle()
        {
            SetProgressText("正在发送数据");
            SetProgress(50);
            foreach (byte key in _teams.Keys)
            {
                _server.SendTeam(key, _teams[key].Name, _teams[key].Team);
            }

            _server.SendRules(_battleRules);
            _server.SendRandomSeed(_seed);

            SetProgressText("正在初始化对战");
            SetProgress(80);

            OnStartBattle();

            SetProgress(100);
            SetProgressText("初始化完毕");
            HideProgress();
        }
        protected virtual void OnStartBattle()
        {
            Dictionary<byte, TeamData> teams = new Dictionary<byte, TeamData>();
            foreach (byte key in _teams.Keys)
            {
                teams[key] = TeamData.FromBytes(_teams[key].Team.ToArray());
            }
            OnBattleStarted += BattleStarted;
            StartBattle(teams);
        }
        private void BattleStarted()
        {
            _server.SendBattleInfo(GetBattleInfo());
            logger.LogAdded += ImageText_Update;
        }
        private void ImageText_Update(string text, Color color)
        {
            _server.SendBattleSnapshot(GetSnapshot(text, color));
        }

        private void OnGetMove(PlayerMove move)
        {
            if ((_server != null))
            {
                _server.SendMove(move);
                SetMove(move);
            }
        }
        private void OnTie(int identity, string player, TieMessage message)
        {
            if (_server == null)
                return;

            if (message == TieMessage.TieRequest)
            {
                if (_tieing)
                {
                    _server.TieRequestFail(identity);
                    return;
                }
                else
                {
                    _tieing = true;
                }
            }

            if (!_tieing)
                return;
            if (message == TieMessage.AgreeTie)
            {
                _agreeTieCounter += 1;
                if (!AllAgreeTie)
                    return;
            }
            else if (message == TieMessage.RefuseTie)
            {
                _tieing = false;
                _agreeTieCounter = 0;
            }

            if (message == TieMessage.AgreeTie)
            {
                _server.Tie(player, message);
            }
            else
            {
                _server.TieExcept(identity, player, message);
            }
            GetTie(player, message);
        }
        private void OnExit(string identity)
        {
            _server.Exit(identity);
            PlayerExit(identity);
        }

        protected override void PlayerMoved(PlayerMove moveValue)
        {
            OnGetMove(moveValue);
        }
        protected override void SendTieMessage(TieMessage message)
        {
            if (_server == null)
                return;

            if (message == TieMessage.TieRequest)
            {
                if (_tieing)
                {
                    GetTie("", TieMessage.Fail);
                    return;
                }
                else
                {
                    _tieing = true;
                }
            }

            if (!_tieing)
                return;
            if (message == TieMessage.AgreeTie)
            {
                _agreeTieCounter += 1;
                if (!AllAgreeTie)
                    return;
            }
            else if (message == TieMessage.RefuseTie)
            {
                _tieing = false;
                _agreeTieCounter = 0;
            }

            _server.Tie(_identity, message);
            if (message == TieMessage.AgreeTie)
                GetTie(_identity, message);
        }

        private bool AllAgreeTie
        {
            get
            {
                int count = _battleMode == BattleMode.Double_4P ? 3 : 1;
                return _agreeTieCounter == count;
            }
        }

        protected override void OnEscape(System.Windows.Forms.FormClosingEventArgs e)
        {
            base.OnEscape(e);
            if (e.Cancel)
                return;
            if (_server != null)
            {
                _server.Stop();
                _server = null;
            }
        }

        protected override void OnBattleEnd()
        {
            base.OnBattleEnd();
            if (_server != null)
            {
                _server.Stop();
                _server = null;
            }
        }

        #region Embeded Class

        private class PlayerInfo
        {
            public PlayerInfo(string name, byte[] team)
            {
                this.Name = name;
                this.Team = team;
            }

            public string Name { get; private set; }

            public byte[] Team { get; private set; }
        }

        #endregion
    }
}
